/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package isvp.utilities;

import isvp.test.PrintClass;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.SAXException;



/**
 *
 * @author jjophin
 */
public class XMLUtils {

    private static XPath xpath = XPathFactory.newInstance().newXPath();
    private static DocumentBuilder docBuilder;
    
    static {
        try {
            docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        } catch (ParserConfigurationException ex) {
        }
    }
    
    /**
     * Returns a Node or NodeList(depending on the returnType) from the object doc corresponding to the xpath.
     * @param doc Document object
     * @param xpathStr String representing the xpath
     * @param returnType Node or NodeList
     * @return 
     */
    private static Object getXML(Object doc, String xpathStr, QName returnType) {
        if(xpathStr == null || xpathStr.isEmpty())
            xpathStr = "./*";        
        try {
            Object result = xpath.evaluate(xpathStr, doc, returnType);
            //TestClass.printXML(nodes.item(0).getOwnerDocument());
            return result;
        } catch (XPathExpressionException ex) {
            return null;
        }        
    }
    
    /**
     * To get a node specified by the xpath from the document 
     * @param doc
     * @param xpathString
     * @return selected node
     */
    public static Node getXMLNode(Object doc, String xpathString){
        Node node = (Node) getXML(doc, xpathString, XPathConstants.NODE);
        //TestClass.printNodeText(node);
        return node;
    }
    
    /**
     * To get a set of nodes specified by the xpath from the document 
     * @param doc
     * @param xpathString
     * @return returns NodeList corresponding to the selected nodes
     */
    public static NodeList getXMLNodes(Object doc, String xpathString) {
        NodeList nodes = (NodeList) getXML(doc, xpathString, XPathConstants.NODESET);
        return nodes;
    }
    
    /**
     * To get the contents of the node specified by the xpath from the document
     * @param doc
     * @param xpathString
     * @return String
     */
    public static String getTextContent(Object doc, String xpathString) {
        Node node = getXMLNode(doc, xpathString);
        String nodeText = "";
        try {
            nodeText = node.getTextContent();
        } catch (NullPointerException ex) {
            return "";
        }
        return nodeText;
    }
    
    /**
     * To get an XML string content from a node after replacing all the HTML escape sequences
     * @param node
     * @return 
     */
    public static String getXMLText(Node node) {
        String content;
        try {
            content = node.getTextContent();
        } catch (NullPointerException ex) {
            return "";
        }        
        content = convertXMLString(content);
        return content;
    }
    
    /**
     * To convert a xml string containing HTML escape sequences to proper xml string
     * @param xmlString
     * @return converted string
     */
    public static String convertXMLString(String xmlString) {
        xmlString = xmlString.replaceAll("&lt;", "<");
        xmlString = xmlString.replaceAll("&gt;", ">");
        xmlString = xmlString.replaceAll("&quot;", "\"");
        xmlString = xmlString.replaceAll("&amp;", "&");
        return xmlString;
    }
    
    /**
     * To convert a xml string to a Document object. If the string contains HTML escape sequences,
     * it is first converted to a proper XML String.
     * @param xmlString
     * @return 
     */
    public static Document getXMLDocument(String xmlString) throws SAXException {
        xmlString = convertXMLString(xmlString);
        byte[] bytes = xmlString.getBytes();
        InputStream is = new ByteArrayInputStream(bytes);
        return getXMLDocument(is);        
    }
    
    /**
     * To create a Document object from an InputStream
     * @param xmlStream
     * @return Document object
     */
    public static Document getXMLDocument(InputStream xmlStream) throws SAXException {
        Document doc = null;
        try {
            doc = docBuilder.parse( xmlStream );
        } catch (IOException ex) {
        }   
        return doc;
    }
    
    /**
     * Returns an indented XML string corresponding to the document passed
     * @param doc
     * @return XML string
     */
    public static String getXMLString(Document doc) {
        DOMImplementationLS domImplementation = (DOMImplementationLS) doc.getImplementation();
        LSSerializer lsSerializer = domImplementation.createLSSerializer();
        return lsSerializer.writeToString(doc); 
    }
    
    /**
     * Returns a properly indented XML string
     * @param xmlString
     * @return indented XML string
     * @throws SAXException if the XML string is not found in proper XML format
     */
    public static String getIndentedXML(String xmlString) throws SAXException {
        if(xmlString.isEmpty())
            return "";
        Document doc = getXMLDocument(xmlString);
        //PrintClass.printXML(doc);
        xmlString = getXMLString(doc);  
        return xmlString;
    }
}
